/***************************************************************************
 *                                                                         *
 *   This file is part of the CrystalMem project,                          *
 *       http://code.google.com/p/crystalmem                               *
 *                                                                         *
 *   Copyright (C) 2008 by Alberto Scarpa <scarpa.alberto@gmail.com>       *
 *   Copyright (C) 2008 by Enrico Ros <enrico.ros@gmail.com>               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

// Use Glew on Win32
#include <QtGlobal>
#ifdef Q_OS_WIN32
    #include <GL/glew.h>
#endif

#include "GLGraphicsView.h"
#include <QFile>
#include <QGLWidget>
#include <QPaintEvent>
#include <QSvgRenderer>
#include <QTimer>


class OurGLWidget : public QGLWidget {
    public:
        OurGLWidget()
            : QGLWidget()
        {
        }

    public:
        void initializeGL() {
            // set Texture memory to 64MB ram (this will be enough!)
            int kBytesVRam = 64 * 1064 ;
            QGLContext::currentContext()->setTextureCacheLimit( kBytesVRam );

            // init Glew on Win32
#ifdef Q_OS_WIN32
            glewInit();
#endif
        }
};

class OurGraphicsScene : public QGraphicsScene {
    public:
        OurGraphicsScene( GLGraphicsView * glGraphicsView )
            : QGraphicsScene( glGraphicsView )
            , m_glGraphicsView( glGraphicsView )
        {
            Q_ASSERT( glGraphicsView );
        }

        // pass all drag/drop events (to empty space) to the GLGraphicsView
        void dragMoveEvent( QGraphicsSceneDragDropEvent * event )
        {
            QGraphicsScene::dragMoveEvent( event );
            if ( !event->isAccepted() )
                m_glGraphicsView->viewDrag( GLGraphicsView::DragMove, event );
        }
        void dragLeaveEvent( QGraphicsSceneDragDropEvent * event )
        {
            QGraphicsScene::dragLeaveEvent( event );
            m_glGraphicsView->viewDrag( GLGraphicsView::DragLeave, event );
        }
        void dropEvent( QGraphicsSceneDragDropEvent * event )
        {
            QGraphicsScene::dropEvent( event );
            m_glGraphicsView->viewDrag( GLGraphicsView::DragDrop, event );
        }

        // pass the eavesdropped 'textual' keys
        void keyPressEvent( QKeyEvent * keyEvent ) {
            QGraphicsScene::keyPressEvent( keyEvent );
            const QString & keyText = keyEvent->text();
            if ( !keyText.isEmpty() )
                m_glGraphicsView->keyPressed( keyText );
        }

    private:
        QString m_lastKeys;
        GLGraphicsView * m_glGraphicsView;
};


GLGraphicsView::GLGraphicsView( const QString & backSvgName, const QString & watermarkFile, QWidget * parentWidget )
    : QGraphicsView( parentWidget )
    , m_inited( false )
    , m_fpsCounter( 0 )
    , m_backName( backSvgName )
    , m_backRenderer( 0 )
    , m_backPixmap( 0 )
    , m_watermarkPixmap( 0 )
    , m_refreshTimer( 0 )
{
    // 1. customize looks

    // 1.1. use OpenGL for rendering
    setViewport( new OurGLWidget() );
    //setCacheMode( QGraphicsView::CacheNone );
    //setOptimizationFlags( QGraphicsView::DontClipPainter | QGraphicsView::DontSavePainterState );
    //setViewportUpdateMode( QGraphicsView::FullViewportUpdate );

    // 1.2. ensure that the viewport doesn't draw anything
    QPalette pal;
    pal.setBrush( QPalette::Window, Qt::transparent );
    viewport()->setPalette( pal );
    viewport()->setBackgroundRole( QPalette::Window );
    viewport()->setAutoFillBackground( false );

    // 1.3. remove frame and scrollbar
    setFrameStyle( QFrame::NoFrame );
    setLineWidth( 0 );
    setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
    setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );

    // 1.4. load watermark if found
    if ( QFile::exists( watermarkFile ) ) {
        m_watermarkPixmap = new QPixmap( watermarkFile );
        if ( m_watermarkPixmap->isNull() ) {
            qWarning( "GLGraphicsView::GLGraphicsView: invalid watermark file '%s'", qPrintable( watermarkFile ) );
            delete m_watermarkPixmap;
            m_watermarkPixmap = 0;
        }
    }

    // 2. create the widgetry
    // 2.1. create the scene
    setScene( new OurGraphicsScene( this ) );
    scene()->setItemIndexMethod( QGraphicsScene::NoIndex );

    // 2.2. customize interation
    setInteractive( true );
}

GLGraphicsView::~GLGraphicsView()
{
    // delete all
    delete m_watermarkPixmap;
    delete m_backPixmap;
    delete m_backRenderer;
    delete m_refreshTimer;
}

void GLGraphicsView::setAutoRefresh( int fps )
{
    // disable updates on items move/resize, only use internal timers for updating
    setViewportUpdateMode( QGraphicsView::NoViewportUpdate );

    // create autorefresh timer
    if ( !m_refreshTimer ) {
        m_refreshTimer = new QTimer( this );
        connect( m_refreshTimer, SIGNAL(timeout()), viewport(), SLOT(update()) );
    }

    // start the timer
    int interval = 1000 / fps;
    if ( (interval * fps) != 1000 )
        qWarning( "GLGraphicsView::setAutoRefresh: the fps value %d can't be achieved with milliseconds precision. Snapping to a near value (%f)", fps, 1000.0 / (double)interval );
    m_refreshTimer->start( interval );
}

void GLGraphicsView::clearAutoRefresh()
{
    if ( m_refreshTimer ) {
        delete m_refreshTimer;
        m_refreshTimer = 0;
    }
}

uint GLGraphicsView::autoRefreshFps() const
{
    return m_refreshTimer ? (1000 / m_refreshTimer->interval()) : 0;
}

void GLGraphicsView::viewDrag( DragType /*dragType*/, QGraphicsSceneDragDropEvent * /*event*/ )
{
    // dummy implementation, does nothing
}

void GLGraphicsView::updateDynamics( double /*dT*/ )
{
    // dummy implementation, does nothing
}

void GLGraphicsView::keyPressed( const QString & /*keyText*/ )
{
    // dummy implementation, does nothing
}

void GLGraphicsView::resizeEvent(QResizeEvent * /*event*/)
{
    // create or resize the cached pixmap
    if ( !m_backPixmap || m_backPixmap->size() != size() ) {
        if ( QFile::exists( m_backName ) ) {
            // create the renderer if needed
            if ( !m_backRenderer )
                m_backRenderer = new QSvgRenderer( m_backName );

            // create the new m_backPixmap
            delete m_backPixmap;
            m_backPixmap = new QPixmap( width(), height() );
            m_backPixmap->fill( Qt::white );
            QPainter cachePainter( m_backPixmap );
            m_backRenderer->render( &cachePainter );
            if ( m_watermarkPixmap )
                cachePainter.drawPixmap( width() - m_watermarkPixmap->width() - 4, 0, *m_watermarkPixmap );
        }
    }

    // resize the scene
    scene()->setSceneRect( 0, 0, (double)width(), (double)height() );
}

void GLGraphicsView::drawBackground( QPainter * painter, const QRectF & )
{
    // the first time initialize OpenGL
    if ( !m_inited ) {
        OurGLWidget * glWidget = dynamic_cast< OurGLWidget * >( viewport() );
        if ( glWidget )
            glWidget->initializeGL();
        else
            qWarning( "GLGraphicsView::drawBackground: the viewport is not an OpenGL widget" );
        m_inited = true;
    }

    // first let's update dynamics
    if ( m_updateTime.isNull() )
        m_updateTime.start();
    else {
        double dT = m_updateTime.restart() / 1000.0;
        if ( dT > 0.2 )
            dT = 0.2;
        updateDynamics( dT );
    }

#if 0
    // you may want to compute FPS here ... (and show it later)
    if ( m_fpsTime.isNull() )
        m_fpsTime.start();
    else {
        m_fpsCounter++;
        if ( m_fpsCounter == 200 ) {
            double fps = 1000.0 * (double)m_fpsCounter / (double)m_fpsTime.elapsed();
            m_fpsTime = QTime();
            m_fpsCounter = 0;
            qWarning( "GLGraphicsView::drawBackground: %3.2f fps", fps );
        }
    }
#endif

    // draw background
    // TODO: OPTIMIZE - draw this using GL functions
    if ( m_backPixmap ) {
        painter->setCompositionMode( QPainter::CompositionMode_Source );
        painter->drawPixmap( 0, 0, *m_backPixmap );
    }
}

